7.07. Инъекции
Инъекции
Инъекции — одна из наиболее фундаментальных и широко распространённых уязвимостей в сфере информационной безопасности. Под инъекцией понимается внедрение злоумышленником несанкционированного кода или данных в программную систему таким образом, что система интерпретирует или исполняет их как часть своей логики. Это приводит к нарушению целостности, конфиденциальности и доступности информации и ресурсов.
В основе инъекционных атак лежит недостаточная проверка, фильтрация или экранирование входных данных, предоставляемых пользователем. Злоумышленник, используя слабые места в архитектуре приложения, может манипулировать его поведением, получать доступ к конфиденциальным данным, выполнять произвольные команды на сервере, подменять контент или полностью компрометировать систему.
Инъекции не являются атакой конкретного типа — это общий паттерн уязвимости, проявляющийся в различных контекстах: базы данных, командные оболочки, языки разметки, динамические языки программирования и т.д. Ниже будут рассмотрены наиболее значимые виды инъекций, их механизмы, последствия и методы защиты.
Общая модель инъекции
Любая инъекция предполагает три основных компонента:
- Контекст исполнения — среда, в которой интерпретируются или исполняются входные данные (например, SQL-интерпретатор, командная оболочка, JavaScript-движок браузера).
- Данные, контролируемые атакующим — строка, отправляемая пользователем (через параметры URL, формы, заголовки HTTP и др.), которая затем встраивается в исполняемый код без надлежащей обработки.
- Нарушение синтаксической или семантической границы — злоумышленник вводит специальные символы или конструкции, которые изменяют исходную логику построения запроса или команды.
Успешная инъекция происходит тогда, когда приложение не разделяет данные и код, позволяя атакующему «переписать» поведение системы с помощью внешнего ввода.
SQL-инъекции (SQL Injection, SQLi)
SQL-инъекция — наиболее известный и исторически значимый тип инъекции. Она возникает при некорректной передаче пользовательских данных в SQL-запросы, что позволяет атакующему изменить логику запроса к реляционной базе данных.
Механизм атаки
Рассмотрим простой пример на языке C# (аналогично проявляется в PHP, Java и других языках):
string query = "SELECT * FROM Users WHERE Login = '" + userInput + "'";
Если userInput содержит строку ' OR '1'='1, то результирующий запрос примет вид:
SELECT * FROM Users WHERE Login = '' OR '1'='1'
Это приведёт к выборке всех записей из таблицы Users, поскольку условие '1'='1' всегда истинно. Злоумышленник может не только обойти аутентификацию, но и модифицировать данные (INSERT, UPDATE, DELETE), извлекать данные из других таблиц (UNION SELECT), выполнять административные команды (в случае расширенных прав СУБД) или даже получить доступ к файловой системе.
Уровни угрозы
SQL-инъекции подразделяются на:
- Классические (In-band): данные извлекаются через тот же канал, по которому отправляется инъекция (например, вывод ошибки или через
UNION). - Слепые (Blind): приложение не возвращает прямых данных, но реакция на истинные/ложные условия позволяет по битам извлекать информацию.
- Out-of-band: данные передаются через сторонние каналы (например, DNS- или HTTP-запросы), если есть сетевой доступ от СУБД.
Защита от SQLi
Эффективная защита строится на нескольких уровнях:
- Параметризованные запросы (Prepared Statements): гарантируют строгое разделение между кодом и данными. Значения передаются отдельно от текста запроса и интерпретируются исключительно как данные.
- Экранирование специальных символов: менее надёжный подход, подвержен ошибкам и зависит от контекста (СУБД, кодировки).
- Принцип наименьших привилегий: учётная запись приложения в СУБД должна иметь минимально необходимые права (без
DROP,FILE,xp_cmdshellи т.п.). - Статический и динамический анализ кода: обнаружение потенциально уязвимых паттернов на этапе разработки и тестирования.
Инъекции команд операционной системы (Command Injection, CMDi)
Command Injection возникает, когда веб-приложение или программное обеспечение передаёт непроверенные пользовательские данные в системные команды (например, через system(), exec(), Process.Start() и аналоги).
Пример уязвимости
import os
os.system("ping -c 1 " + user_input)
Если user_input содержит 8.8.8.8; cat /etc/passwd, то будет выполнен не только ping, но и команда чтения файла паролей. Символы ;, &&, |, \n позволяют атакующему конструировать произвольные последовательности команд.
Контекст уязвимости
Особенно опасны такие инъекции на серверах с высокими привилегиями, где приложение запущено от имени root или SYSTEM. Последствия — полный контроль над сервером, установка бэкдоров, эксплуатация внутренней сети.
Защита
- Избегать вызова системных команд на основе пользовательского ввода.
- Если вызов необходим — использовать белый список допустимых значений.
- Применять строгую валидацию и экранирование (но надёжнее — отказаться от динамического построения команд).
- Запускать приложения в изолированных средах (контейнеры, chroot, sandbox).
Межсайтовый скриптинг (Cross-Site Scripting, XSS)
XSS — это форма инъекции, при которой вредоносный JavaScript-код внедряется в веб-страницу, отображаемую в браузере жертвы. В отличие от SQLi и CMDi, XSS напрямую не атакует сервер, а эксплуатирует доверие пользователя к сайту.
Типы XSS
- Отражённый (Reflected): вредоносный скрипт передаётся через URL (например, параметр поиска) и сразу возвращается в ответе сервера. Атака требует, чтобы жертва перешла по специально подготовленной ссылке.
- Сохранённый (Stored): скрипт сохраняется на сервере (в комментариях, профилях, форумах) и отдаётся всем пользователям, просматривающим заражённый контент.
- DOM-based: уязвимость возникает на стороне клиента — вредоносные данные модифицируют DOM-дерево без участия сервера (например, через
location.hash,document.write).
Последствия
- Кража куков сессии (включая
HttpOnlyв некоторых случаях). - Перенаправление на фишинговые страницы.
- Логгирование нажатий клавиш (keylogging).
- Выполнение действий от имени пользователя (например, перевод средств).
Защита
- Экранирование контента: перед вставкой в HTML все специальные символы (
<,>,",',&) должны быть заменены на HTML-сущности (<,>и т.д.). - Контекстно-зависимая обработка: правила экранирования различаются для HTML, атрибутов, JavaScript, CSS, URL.
- Content Security Policy (CSP): механизм, ограничивающий источники загрузки скриптов, стилей и других ресурсов.
- Фреймворковые средства: современные фреймворки (React, Angular, Vue) по умолчанию экранируют вывод, но ручные манипуляции (
innerHTML,dangerouslySetInnerHTML) могут обойти защиту.
Обобщённые принципы защиты от инъекций
Несмотря на различия в контекстах, все инъекции подчиняются общим принципам предотвращения:
- Разделение кода и данных: никогда не встраивайте пользовательские данные непосредственно в исполняемый код.
- Белые списки: вместо попыток заблокировать «плохие» символы — разрешайте только известные безопасные значения.
- Экранирование в соответствии с контекстом: если встраивание неизбежно, применяйте контекстно-зависимое экранирование, соответствующее целевой среде (SQL, OS, HTML и пр.).
- Минимизация поверхности атаки: отключайте ненужные функции (например,
xp_cmdshellв MS SQL,execв PHP), ограничивайте права, используйте沙箱. - Тестирование и мониторинг: регулярное проведение тестов на проникновение, статический анализ кода, логирование подозрительных запросов.
Редкие, но критичные формы инъекций
Помимо широко известных SQL-, командных и XSS-инъекций, существует ряд специализированных форм внедрения вредоносного кода, возникающих в узкоспециализированных средах: XML-процессоры, LDAP-каталоги, XPath-выражения, NoSQL-базы данных и др. Эти уязвимости часто остаются вне поля зрения разработчиков, поскольку подсистемы, в которых они возникают, считаются «внутренними» или «некритичными». Однако в современных распределённых архитектурах такие компоненты нередко становятся точками входа для атак.
XPath-инъекции
XPath (XML Path Language) — язык запросов к XML-документам, аналогичный SQL для реляционных баз. Если веб-приложение строит XPath-выражения на основе непроверенного пользовательского ввода, возможно внедрение произвольной логики.
Пример
Предположим, приложение ищет пользователя в XML-файле:
<users>
<user>
<login>admin</login>
<password>secret</password>
</user>
</users>
XPath-запрос может выглядеть так:
string xpath = "/users/user[login='" + login + "' and password='" + password + "']";
Если злоумышленник введёт в поле логина ' or '1'='1, то запрос примет вид:
/users/user[login='' or '1'='1' and password='...']
Если система не проверяет количество возвращённых узлов, а считывает первый попавшийся — аутентификация будет обойдена. В отличие от SQL, XPath не имеет встроенных механизмов ограничения прав, поэтому уязвимость позволяет извлекать любые данные из всего XML-документа, включая поля, не предназначенные для отображения.
Защита
- Использовать параметризованные XPath-выражения, если они поддерживаются (редко).
- Применять строгую валидацию входных данных (белый список допустимых символов).
- Избегать построения XPath-запросов на основе прямого пользовательского ввода.
LDAP-инъекции
LDAP (Lightweight Directory Access Protocol) — протокол для доступа к иерархическим каталогам (например, Active Directory). LDAP-инъекция возникает при некорректной обработке входных данных в поисковых фильтрах.
Пример
Фильтр может строиться так:
filter = "(uid=" + username + ")"
Если username содержит *)(uid=*))(|(uid=*, то фильтр превращается в:
(uid=*)(uid=*))(|(uid=*
Это может привести к обходу аутентификации или получению списка всех пользователей.
Особенности
LDAP использует собственный набор метасимволов: *, (, ), \, &, |, !. Их экранирование регламентировано в RFC 4515: каждый из этих символов должен быть заменён на \xx (шестнадцатеричное представление ASCII-кода).
Защита
- Экранирование всех метасимволов в соответствии с RFC 4515.
- Использование готовых библиотек для формирования фильтров (например,
ldap.filter.escapeв Python). - Применение строгой валидации (например, логин должен соответствовать регулярному выражению
^[a-zA-Z0-9_]+$).
NoSQL-инъекции
NoSQL-инъекции не являются прямым аналогом SQL-инъекций, поскольку большинство NoSQL-баз (MongoDB, CouchDB, Redis и др.) не используют строковые запросы в традиционном смысле. Вместо этого они принимают структурированные запросы — чаще всего в виде JSON-объектов или словарей. Уязвимость возникает при некорректной обработке пользовательских данных, которые интерпретируются как часть структуры запроса, а не как простые значения.
Пример (MongoDB)
Предположим, в приложении на Node.js происходит поиск по коллекции:
db.users.findOne({ username: req.body.username, password: req.body.password });
Если злоумышленник отправит JSON:
{
"username": { "$ne": "" },
"password": { "$ne": "" }
}
То MongoDB интерпретирует это как:
{ username: { $ne: "" }, password: { $ne: "" } }
Оператор $ne (not equal) означает «любое значение, кроме пустой строки». Таким образом, будет найден первый пользователь с непустыми логином и паролем — аутентификация обойдена.
В более сложных сценариях возможно использование операторов $where (выполнение JavaScript-кода на сервере MongoDB) или $regex для перебора данных побитно (аналог blind SQLi).
Защита
- Строгая типизация входных данных: если ожидается строка — принимать только строки, отклоняя объекты.
- Валидация структуры запроса: использование схем (например, Joi, Zod) для фильтрации и нормализации входных данных.
- Отключение опасных операторов: в MongoDB можно запретить использование
$where,$mapReduceи других функций на уровне базы. - Принцип наименьших привилегий: учётная запись приложения не должна иметь прав на выполнение административных команд.
Инъекции в динамические языки (eval-инъекции)
Многие скриптовые языки (JavaScript, Python, PHP, Ruby) поддерживают функции динамической интерпретации кода: eval(), exec(), Function(), import(), pickle.loads() и др. Использование таких функций с непроверенными данными представляет собой прямую инъекцию исполняемого кода.
Пример
user_input = request.GET['data']
result = eval(user_input)
Если user_input содержит __import__('os').system('rm -rf /'), последствия могут быть катастрофическими.
Особенности
Такие инъекции особенно опасны в средах с высокими привилегиями (например, административных панелях, внутренних инструментах). Часто разработчики считают, что «ввод контролируется», но забывают о возможностях сериализованных объектов (например, pickle в Python), которые при десериализации могут выполнять произвольный код.
Защита
- Категорический отказ от
eval()и аналогов в production-коде. - Использование безопасных альтернатив: JSON вместо
evalдля парсинга, шаблонизаторов вместо конкатенации кода. - При необходимости десериализации — использовать только доверенные форматы (JSON, XML с отключёнными DTD) и избегать собственных сериализованных объектов.
- Изоляция среды выполнения (например, запуск в контейнере без доступа к файловой системе).
Объединяющий взгляд: модель «доверенной границы»
Все инъекции можно рассматривать через призму нарушения доверенной границы между данными и кодом. Приложение получает данные от внешнего агента (пользователя, API, файла) и передаёт их в интерпретатор (SQL, JS, OS shell и т.д.). Если граница между данными и кодом не защищена — интерпретатор не может отличить легитимные данные от вредоносных инструкций.
Фундаментальный принцип защиты — никогда не доверять внешним данным. Это означает, что:
- Все входные данные должны считаться потенциально вредоносными до тех пор, пока не доказано обратное.
- Построение исполняемой логики должно быть полностью отделено от передачи значений.
- Контекст интерпретации должен быть максимально ограничен и изолирован.
Практические методы выявления и тестирования инъекций
Теоретическое понимание механизма инъекций необходимо, но недостаточно для обеспечения безопасности программных систем. Обнаружение уязвимостей требует системного подхода, сочетающего автоматизированные инструменты, ручной анализ и понимание контекста работы приложения.
1. Ручное тестирование (Manual Testing)
Ручное тестирование остаётся наиболее надёжным методом выявления сложных и контекстно-зависимых инъекций. Оно предполагает:
- Анализ всех точек ввода: не только формы и URL-параметры, но и HTTP-заголовки (
User-Agent,Referer), cookie, тела запросов (включая JSON, XML), файлы, веб-сокеты. - Фаззинг входных данных: подстановка специальных последовательностей, характерных для разных типов инъекций:
- Для SQLi:
',",--,/*,UNION SELECT,SLEEP(),BENCHMARK(). - Для CMDi:
;,&,|,$(...),`...`,%0a,&&,||. - Для XSS:
<script>,javascript:,onerror=,"><svg onload=...>. - Для NoSQL:
{ "$ne": 1 },{ "$gt": "" },{ "$where": "..." }.
- Для SQLi:
- Наблюдение за поведением приложения: изменения в ответе, задержки (time-based blind), ошибки (error-based), перенаправления — всё это может быть индикатором уязвимости.
- Использование прокси-инструментов: Burp Suite, OWASP ZAP позволяют перехватывать, модифицировать и повторно отправлять запросы, что критично для тестирования инъекций.
2. Автоматизированное сканирование
Автоматизированные сканеры уязвимостей (DAST — Dynamic Application Security Testing) могут быстро выявить классические инъекции, особенно отражённые формы XSS и SQLi. Однако они имеют ограничения:
- Сложно обнаруживают слепые (blind) инъекции без чёткой сигнализации.
- Часто дают ложные срабатывания (false positives) или пропускают уязвимости в нестандартных контекстах (например, GraphQL, WebSocket, SOAP).
- Не понимают бизнес-логику приложения, что делает их беспомощными против логических уязвимостей.
Тем не менее, такие инструменты, как sqlmap (для SQLi), NoSQLMap (для NoSQL), dalfox (для XSS), являются мощными утилитами в руках специалиста и позволяют автоматизировать эксплуатацию подтверждённых уязвимостей.
3. Статический анализ кода (SAST)
SAST-инструменты анализируют исходный код или байт-код на наличие опасных паттернов:
- Конкатенация строк с пользовательским вводом перед передачей в
SqlCommand,os.system,eval,innerHTML. - Использование устаревших или небезопасных API.
- Отсутствие вызовов функций экранирования.
Примеры инструментов: SonarQube, Checkmarx, Semgrep, Bandit (для Python), ESLint с плагинами безопасности (для JS/TS).
Важно: SAST не гарантирует отсутствие уязвимостей, но позволяет выявить рискованные участки кода на ранних этапах разработки.
4. Тестирование в CI/CD
Интеграция проверок на инъекции в процесс непрерывной интеграции позволяет выявлять регрессии и новые уязвимости до попадания кода в production. Это включает:
- Запуск линтеров и SAST-сканеров при каждом коммите.
- Автоматизированные unit- и интеграционные тесты с вредоносными входными данными.
- Использование «загрязнённых» тестовых данных (taint analysis) для отслеживания путей передачи непроверенного ввода.
Стандарты и классификации
Инъекции официально признаны одной из самых критичных уязвимостей. В OWASP Top 10 инъекции (в широком смысле) неоднократно занимали первое место:
- OWASP Top 10 2013, 2017: Injection (A1).
- OWASP Top 10 2021: хотя формально вытеснены более широкими категориями (например, «Software and Data Integrity Failures»), инъекции по-прежнему входят в основные векторы атак.
В системе CWE (Common Weakness Enumeration) инъекции представлены множеством записей:
- CWE-79 — XSS,
- CWE-89 — SQL Injection,
- CWE-78 — OS Command Injection,
- CWE-90 — LDAP Injection,
- CWE-611 — XML External Entity (XXE), близкая по сути,
- CWE-94 — Code Injection.
Эти идентификаторы используются в отчётах, стандартах сертификации (PCI DSS, ISO/IEC 27001) и системах управления уязвимостями.
Архитектурные подходы к предотвращению инъекций
Помимо кодовых практик, важно внедрять архитектурные меры:
- Zero Trust к входным данным: любые данные извне (включая внутренние микросервисы) рассматриваются как недоверенные.
- Изоляция компонентов: базы данных, файловые системы, командные оболочки должны быть недоступны напрямую из публичных интерфейсов.
- API-гейты и WAF: Web Application Firewall может блокировать известные сигнатуры инъекций, но не заменяет корректную реализацию. Лучше использовать его как дополнительный барьер.
- Политики выполнения: например, в Node.js можно использовать
vm2вместоeval, в .NET —AppDomainс ограниченными разрешениями (хотя в .NET Core/AppDomain упразднён, требуется другой подход — например, изоляция в контейнерах).